﻿using System.Web;
using DoNet.Helper;
using DoNet.Platform.QQ.Model;

namespace DoNet.Platform.QQ
{
    /// <summary>
    /// 腾讯QQ Access Token
    /// </summary>
    public class TokenApi
    {
        /// <summary>
        /// 通过Authorization Code获取Access Token
        /// </summary>
        /// <param name="client_id">申请QQ登录成功后，分配给网站的appid。</param>
        /// <param name="client_secret">申请QQ登录成功后，分配给网站的appkey。</param>
        /// <param name="code">上一步返回的authorization code。</param>
        /// <param name="redirect_uri">与上面一步中传入的redirect_uri保持一致。 </param>
        /// <param name="need_openid">need_openid=1，表示同时获取openid。 </param>
        /// <returns></returns>
        public static ReturnData GetToken(string client_id, string client_secret, string code, string redirect_uri, int need_openid = 0)
        {
            /*
            Step2：通过Authorization Code获取Access Token
            请求地址：
            PC网站：https://graph.qq.com/oauth2.0/token
            请求方法：
            GET
            请求参数：
            请求参数请包含如下内容：

            参数	是否必须	含义
            grant_type	必须	授权类型，在本步骤中，此值为“authorization_code”。
            client_id	必须	申请QQ登录成功后，分配给网站的appid。
            client_secret	必须	申请QQ登录成功后，分配给网站的appkey。
            code	必须	上一步返回的authorization code。
            如果用户成功登录并授权，则会跳转到指定的回调地址，并在URL中带上Authorization Code。
            例如，回调地址为www.qq.com/my.php，则跳转到：
            http://www.qq.com/my.php?code=520DD95263C1CFEA087******
            注意此code会在10分钟内过期。
            redirect_uri	必须	与上面一步中传入的redirect_uri保持一致。
            fmt	可选	因历史原因，默认是x-www-form-urlencoded格式，如果填写json，则返回json格式
            need_openid	可选	need_openid=1，表示同时获取openid。
            返回说明：

            如果成功返回，即可在返回包中获取到Access Token。 如(不指定fmt时）：

            access_token=FE04************************CCE2&expires_in=5184000&refresh_token=88E4************************BE14


            参数说明	描述
            access_token	授权令牌，Access_Token。
            expires_in	该access token的有效期，单位为秒。
            refresh_token	在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。
            注：refresh_token仅一次有效

            错误码说明：
            接口调用有错误时，会返回error和error_description字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
            PC网站接入时，错误码详细信息请参见：100000-100031：PC网站接入时的公共返回码。

            Step3：（可选）权限自动续期，获取Access Token
            Access_Token的有效期默认是60天，过期后需要用户重新授权才能获得新的Access_Token。本步骤可以实现授权自动续期，避免要求用户再次授权的操作，提升用户体验。
            请求地址：
            PC网站：https://graph.qq.com/oauth2.0/token
            请求方法：
            GET
            请求参数：
            请求参数请包含如下内容：

            参数	是否必须	含义
            grant_type	必须	授权类型，在本步骤中，此值为“refresh_token”。
            client_id	必须	申请QQ登录成功后，分配给网站的appid。
            client_secret	必须	申请QQ登录成功后，分配给网站的appkey。
            refresh_token	必须	首次：使用在Step2中获取到的最新的refresh_token。
            后续：使用刷新后返回的最新refresh_token

            fmt	可选	因历史原因，默认是x-www-form-urlencoded格式，如果填写json，则返回json格式
            返回说明：
            如果成功返回，即可在返回包中获取到Access Token。 如(不指定fmt时）：
            access_token=FE04************************CCE2&expires_in=5184000&refresh_token=88E4************************BE14。

            参数说明	描述
            access_token	授权令牌，Access_Token。
            expires_in	该access token的有效期，单位为秒。
            refresh_token	在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。每次生成最新的refresh_token，且仅一次有效，一次登录，refresh_token整个续票过程，最长有效期：3个月。
            错误码说明：
            接口调用有错误时，会返回error和error_description字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
            PC网站接入时，错误码详细信息请参见：100000-100031：PC网站接入时的公共返回码。 
            */

            string param = string.Format("&client_id={0}&client_secret={1}&code={2}&redirect_uri={3}", client_id, client_secret, code, HttpUtility.UrlEncode(redirect_uri));
            if (need_openid == 1)
            {
                param = string.Format("&need_openid={0}", need_openid);
            }
            string reqUrl = CommonConfig.WebApiUrl_BaiDu + "/oauth/2.0/token?grant_type=authorization_code" + string.Format("&client_id={0}&client_secret={1}&code={2}&redirect_uri={3}", client_id, client_secret, code, HttpUtility.UrlEncode(redirect_uri));
            var result = HttpHelper.HttpGet(reqUrl);
            if (result.success)
            {
                var obj = JsonHelper.DeserializeObject<dynamic>(result.data);
                if (obj != null)
                {
                    /*
                    接口调用有错误时，会返回error和error_description字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
                    */
                    if (obj.error != null)
                    {
                        return new ReturnData()
                        {
                            success = false,
                            message = obj.error_description,
                            data = result.data
                        };
                    }
                    else
                    {
                        return result;
                    }
                }
                else
                {
                    return result;
                }
            }
            else
            {
                return result;
            }
        }

        /// <summary>
        /// 获取授权URL
        /// https://wiki.connect.qq.com/%e4%bd%bf%e7%94%a8authorization_code%e8%8e%b7%e5%8f%96access_token
        /// </summary>
        /// <param name="client_id">申请QQ登录成功后，分配给应用的appid。</param>
        /// <param name="redirect_uri">成功授权后的回调地址，必须是注册appid时填写的主域名下的地址，建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode。</param>
        /// <param name="state">重定向后会带上state参数。建议开发者利用state参数来防止CSRF攻击。</param>
        /// <param name="scope">请求用户授权时向用户显示的可进行授权的列表。
        /// 可填写的值是API文档中列出的接口，如果要填写多个接口名称，请用逗号隔开。
        /// 例如：scope=get_user_info,list_album,upload_pic
        /// 不传则默认请求对接口get_user_info进行授权。
        /// 建议控制授权项的数量，只传入必要的接口名称，因为授权项越多，用户越可能拒绝进行任何授权。</param>
        /// <param name="display">可选	仅PC网站接入时使用。用于展示的样式。不传则默认展示为PC下的样式。如果传入“mobile”，则展示为mobile端下的样式。</param>
        /// <returns></returns>
        public static string GetAuthorizeUrl(string client_id, string redirect_uri, string state, string scope = "", string display = "")
        {
            /*
            返回说明：
            1. 如果用户成功登录并授权，则会跳转到指定的回调地址，并在redirect_uri地址后带上Authorization Code和原始的state值。如：
            PC网站：http://graph.qq.com/demo/index.jsp?code=9A5F************************06AF&state=test
            注意：此code会在10分钟内过期。
            2. 如果用户在登录授权过程中取消登录流程，对于PC网站，登录页面直接关闭；
            错误码说明：
            接口调用有错误时，会返回error和error_description字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
            PC网站接入时，错误码详细信息请参见：100000-100031：PC网站接入时的公共返回码。
            */
            string param = string.Format("/oauth2.0/authorize?response_type=code&client_id={0}&redirect_uri={1}&state={2}", client_id, HttpUtility.UrlEncode(redirect_uri));
            if (!string.IsNullOrWhiteSpace(scope))
            {
                param += string.Format("&scope={0}", scope);
            }
            if (!string.IsNullOrWhiteSpace(display))
            {
                param += string.Format("&display={0}", display);
            }
            return CommonConfig.WebApiUrl_QQ + param;
        }

        /// <summary>
        /// 此接口用于获取个人信息。
        /// 开发者可通过openID来获取用户的基本信息。特别需要注意的是，如果开发者拥有多个移动应用、网站应用，可通过获取用户的unionID来区分用户的唯一性，因为只要是同一QQ互联平台下的不同应用，unionID是相同的。换句话说，同一用户，对同一个QQ互联平台下的不同应用，unionID是相同的。（需邮件申请相关权限。点此申请）
        /// </summary>
        /// <param name="access_token"></param>
        /// <param name="unionid"></param>
        /// <returns></returns>
        public static ReturnData GetOpenId(string access_token, int unionid = 0)
        {
            /*
            GET https://graph.qq.com/oauth2.0/me?access_token=ACCESSTOKEN&unionid=1
            https://graph.qq.com/oauth2.0/me?access_token=ACCESSTOKEN&unionid=1
            参数	是否必需	说明
            access_token	 是	用户凭证
            unionid	 否	是否申请unionID
            fmt	 否	因历史原因，默认是jsonpb格式，如果填写json，则返回json格式

            错误码	描述	说明
            100016	access token check failed	用户凭据过期（60天）或者不正确
            100048	companyid not set	未申请unionID接口调用权限，请邮件申请
            */

            string param = string.Format("?access_token={0}", access_token);
            if (unionid == 1)
            {
                param = string.Format("&unionid={0}", unionid);
            }
            string reqUrl = CommonConfig.WebApiUrl_BaiDu + "/oauth2.0/me" + param;
            var result = HttpHelper.HttpGet(reqUrl);
            if (result.success)
            {
                var obj = JsonHelper.DeserializeObject<dynamic>(result.data);
                if (obj != null)
                {
                    /*
                    {
　　                    "error":100016,
　　                    "error_description":"access token check failed",
                    }
                    */
                    if (obj.error != null)
                    {
                        return new ReturnData()
                        {
                            success = false,
                            message = obj.error_description,
                            data = result.data
                        };
                    }
                    else
                    {
                        return result;
                    }
                }
                else
                {
                    return result;
                }
            }
            else
            {
                return result;
            }
        }


        /// <summary>
        /// 获取登录用户在QQ空间的信息，包括昵称、头像、性别及黄钻信息（包括黄钻等级、是否年费黄钻等）。
        /// 2013年5月，网站和移动应用都可以调用本接口。用户信息字段增加了QQ头像字段。
        /// 2012年9月20日，新增返回参数"is_yellow_year_vip"，支持获取用户是否年费黄钻的信息。
        /// https://wiki.connect.qq.com/get_user_info
        /// </summary>
        /// <param name="access_token">可通过使用Authorization_Code获取Access_Token 或来获取。access_token有60天有效期。</param>
        /// <param name="oauth_consumer_key">申请QQ登录成功后，分配给应用的appid</param>
        /// <param name="openid">用户的ID，与QQ号码一一对应。可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。</param>
        /// <returns></returns>
        public static ReturnData GetUserInfo(string access_token, string oauth_consumer_key, string openid)
        {
            /*
            https://graph.qq.com/user/get_user_info?access_token=*************&oauth_consumer_key=12345&openid=**************** 
            */
            string reqUrl = CommonConfig.WebApiUrl_BaiDu + string.Format("/user/get_user_info?access_token={0}&oauth_consumer_key={1}&openid={2}", access_token, oauth_consumer_key, openid);
            var result = HttpHelper.HttpGet(reqUrl);
            if (result.success)
            {
                var res = JsonHelper.DeserializeObject<UserInfo>(result.data);
                if (res != null)
                {
                    if (res.ret > 0)
                    {
                        return new ReturnData()
                        {
                            success = false,
                            message = res.msg,
                            data = result.data
                        };
                    }
                    else
                    {
                        return result;
                    }
                }
                else
                {
                    return result;
                }
            }
            else
            {
                return result;
            }
        }
    }
}
